import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _inputController = StreamController.broadcast();
  final _scoreController = StreamController.broadcast();

  int score = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: StreamBuilder(
          stream: _scoreController.stream.transform(TallyTransformer()),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              score += snapshot.data as int;
            }
            return Text("Score: $score");
          },
        ),
      ),
      body: Stack(
        children: [
          ...List.generate(
              5, (_) => Puzzle(_inputController.stream, _scoreController)),
          Align(
            alignment: Alignment.bottomCenter,
            child: KeyPad(_inputController, _scoreController),
          )
        ],
      ),
    );
  }
}

class TallyTransformer implements StreamTransformer {
  int sum = 0;
  final StreamController _controller = StreamController();

  @override
  Stream bind(Stream stream) {
    stream.listen((event) {
      sum += event as int;
      _controller.add(sum);
    });
    return _controller.stream;
  }

  @override
  StreamTransformer<RS, RT> cast<RS, RT>() => StreamTransformer.castFrom(this);
}

class Puzzle extends StatefulWidget {
  final Stream inputStream;
  final StreamController scoreStream;

  const Puzzle(this.inputStream, this.scoreStream, {Key? key})
      : super(key: key);

  @override
  State<Puzzle> createState() => _PuzzleState();
}

class _PuzzleState extends State<Puzzle> with SingleTickerProviderStateMixin {
  late int a, b;
  late double x;
  late Color color;

  late AnimationController _controller;

  @override
  void initState() {
    _controller = AnimationController(vsync: this);
    reset(Random().nextDouble());

    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        reset();
        widget.scoreStream.add(-3);
      }
    });

    widget.inputStream.listen((event) {
      if (event == a + b) {
        reset();
        widget.scoreStream.add(5);
      }
    });

    super.initState();
  }

  void reset([from = 0.0]) {
    a = Random().nextInt(5) + 1;
    b = Random().nextInt(5);
    x = Random().nextDouble() * 300; // MediaQuery.of(context).size.height - 100;
    color = Colors.primaries[Random().nextInt(Colors.primaries.length)][200]!
        .withOpacity(0.5);
    _controller.duration =
        Duration(milliseconds: Random().nextInt(5000) + 5000);
    _controller.forward(from: from);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, _) {
        return Positioned(
          left: x,
          top: MediaQuery.of(context).size.height * _controller.value - 100,
          child: Container(
            decoration: BoxDecoration(
              color: color,
              border: Border.all(color: Colors.black12),
              borderRadius: BorderRadius.circular(24),
            ),
            padding: const EdgeInsets.all(10.0),
            child: Text("$a + $b", style: const TextStyle(fontSize: 24)),
          ),
        );
      },
    );
  }
}

class KeyPad extends StatelessWidget {
  final StreamController _inputController; // input controller
  final StreamController _scoreController; // input controller

  const KeyPad(this._inputController, this._scoreController, {Key? key})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GridView.count(
      shrinkWrap: true,
      childAspectRatio: 2,
      // padding: const EdgeInsets.all(0.0),
      physics: const NeverScrollableScrollPhysics(),
      crossAxisCount: 3,
      children: List.generate(9, (index) {
        return Container(
          color: Colors.primaries[index + 5][200],
          child: TextButton(
            child: Text(
              "${index + 1}",
              style: const TextStyle(fontSize: 24, color: Colors.white60),
            ),
            onPressed: () {
              _inputController.add(index + 1);
              _scoreController.add(-2);
            },
          ),
        );
      }),
    );
  }
}
